#define _XOPEN_SOURCE 500
#include <errno.h>
#include <ftw.h>
#include "core.h"
#include "lmdb.h"


bool LSUP_env_is_init = false;

/** @brief Warning messages.
 *
 * The message corresponding to the rc is found by
 * warning_msg[rc - LSUP_MIN_WARNING]. #LSUP_strerror() facilitates this.
 */
char *warning_msg[] = {
    "LSUP_NOACTION: No action or change of state occurred.",
    "LSUP_NORESULT: No result.",
    "LSUP_END: End of the loop reached.",
    "LSUP_CONFLICT: A conflict prevented a resource from being updated.",
};

/** @brief error messages.
 *
 * Note that all error values are < 0 so it is possible to set conditions to
 * be triggered only by error return values.
 *
 * The message corresponding to the rc is found by
 * err_msg[rc - LSUP_MIN_ERROR]. #LSUP_strerror() facilitates this.
 */
char *err_msg[] = {
    "LSUP_ERROR: Runtime error.",
    "LSUP_PARSE_ERR: Error parsing input.",
    "LSUP_VALUE_ERR: Invalid input.",
    "LSUP_TXN_ERR: MDB transaction error.",
    "LSUP_DB_ERR: Database error.",
    "LSUP_NOT_IMPL_ERR: Feature is not implemented.",
    "LSUP_IO_ERR: Input/Output error.",
    "LSUP_MEM_ERR: Memory error.",
    "LSUP_CONFLICT_ERR: A resource conflict interrupted the operation.",
    "LSUP_ENV_ERR: Environment not initialized. Did you call LSUP_init()?",
};

char *LSUP_root_path = __FILE__; // This is trimmed to root path on init.


LSUP_rc
mkdir_p (const char *_path, mode_t mode)
{
    char *path = strdup (_path);
    char *p;

    // Trim any trailing slash(es).
    for (p = path + strlen (path) - 1; p > path; p--)
        if (*p == '/') *p = '\0';
        else break;

    errno = 0;
    LSUP_rc rc = LSUP_NOACTION;

    /* Iterate the string */
    for (p = path + 1; *p; p++) {
        if (*p == '/') {
            /* Temporarily truncate */
            *p = '\0';

            if (mkdir (path, mode) != 0 && errno != EEXIST) goto finally;

            *p = '/';
        }
    }
    if (mkdir (path, mode) != 0) {
        if (errno != EEXIST) rc = errno;
    } else {
        rc = LSUP_OK;
    }

finally:

    log_trace ("Path: %s", path);
    log_trace ("errno: %d", errno);
    log_trace ("rc: %d", rc);
    free (path);

    return rc;
}


int
unlink_cb(
        const char *fpath, const struct stat *sb, int typeflag,
        struct FTW *ftwbuf)
{
    log_debug ("Removing %s", fpath);
    int rv = remove(fpath);

    if (rv)
        perror(fpath);

    return rv;
}

int rm_r(const char *path)
{ return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS); }


const char *
LSUP_strerror (LSUP_rc rc)
{
    if (rc >= LSUP_MIN_ERROR && rc <= LSUP_MAX_ERROR)
        return err_msg[rc - LSUP_MIN_ERROR];

    if (rc >= LSUP_MIN_WARNING && rc <= LSUP_MAX_WARNING)
        return warning_msg[rc - LSUP_MIN_WARNING];

    return mdb_strerror (rc);
}


/* Inline extern functions. */

int utf8_encode (const uint32_t utf, unsigned char *out);
